﻿using Castle.Core.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using Yto.Common.Configuration;
using Yto.Common.Dependency;

namespace Yto.Modules
{
    public class ModuleManager : IModuleManager
    {
        private ModuleList _listModules;
        private ICtnManager _ctnManager;

        public ILogger Logger { get; set; }

        public IReadOnlyList<ModuleItemInfo> Modules => _listModules;

        public ModuleManager(ICtnManager ctnManager)
        {
            _ctnManager = ctnManager;
            Logger = NullLogger.Instance;
        }

        public void Initialize(Type launchModule)
        {
            _listModules = new ModuleList(launchModule);
            LoadDependencies();

        }

        private void LoadDependencies()
        {
            Logger.Info($"Load begin: {DateTime.Now}");
            var moduleList = BaseModule.GetAllDependecy(_listModules.LaunchModule);

            RegistModuleList(moduleList);
            CreateModuleItemInfo(moduleList);

        }

        private void CreateModuleItemInfo(ICollection<Type> moduleList)
        {
            foreach (var item in moduleList)
            {
                var moduleInstance = _ctnManager.Resolver<BaseModule>(item);
                moduleInstance.CtnManager = _ctnManager;
                moduleInstance.Configuration = _ctnManager.Resolver<IStartupConfiguration>();

                var moduleItemInfo = new ModuleItemInfo(item, moduleInstance);
                _listModules.Add(moduleItemInfo);
            }
            foreach (var itemInfo in _listModules)
            {
                foreach (var dependedModuleType in BaseModule.FindDependenies(itemInfo.ModuleType))
                {
                    var dependedModuleItemInfo = _listModules.FirstOrDefault(x => x.ModuleType == dependedModuleType);
                    if (dependedModuleItemInfo != null && itemInfo.Dependencies.FirstOrDefault(m => m.ModuleType == dependedModuleType) == null)
                    {
                        itemInfo.Dependencies.Add(dependedModuleItemInfo);
                    }
                }
            }
        }

        private void RegistModuleList(ICollection<Type> moduleList)
        {
            foreach (var item in moduleList)
            {
                if (!_ctnManager.IsRegisted(item))
                {
                    _ctnManager.Regist(item);
                }
            }
        }

        public void StartModules()
        {
            List<ModuleItemInfo> sortedList = new List<ModuleItemInfo>();
            sortListByDependency(_listModules, sortedList);

            sortedList.ForEach(x => x.Instance.BeforeInitialize());
            sortedList.ForEach(x => x.Instance.Initialize());
            sortedList.ForEach(x => x.Instance.AfterInitialize());
        }

        private void sortListByDependency(IList<ModuleItemInfo> listModules, List<ModuleItemInfo> sortedList)
        {
            foreach(var item in listModules)
            {
                if (item.Dependencies != null)
                {
                    sortListByDependency(item.Dependencies, sortedList);
                }
                if (!sortedList.Contains(item))
                {
                    sortedList.Insert(0, item);
                }
                else
                {
                    sortedList.Remove(item);
                    sortedList.Insert(0, item);
                }
            }
        }
    }
}
